package com.example.reflection.realize;

import com.example.reflection.pojo.User;
import com.example.reflection.service.IMath;
import com.example.reflection.service.impl.UserServiceImpl;

import java.io.Serializable;
import java.lang.reflect.*;
import java.util.ArrayList;

public class TestReflect extends UserServiceImpl implements Serializable, IMath {
    private static final long serialVersionUID = -453553744011915692L;

    private final User user = null;

    public String str = null;
    public int sum = 0;

    private IMath iMath;
    protected Integer id = 1;

    private static final TestReflect REFLECT = new TestReflect();

    public static void main(String[] args) {
        // 1.通过一个对象获得完整的包名和类名
        getPackageClassName();

        // 2.实例化Class类对象
        getInstantiationClassObject();

        // 3.获取一个对象的父类与实现的接口
        getInterfaceImplements();

        // 4.通过反射机制实例化一个类的对象
        getReflectionInstantiationClassObject();

        // 5.获取某个类的全部属性
        getClassAllAttribute();

        // 6.获取某个类的全部方法
        getClassAllMethod();

        try {
            System.out.println("7.通过反射机制调用某个类的方法");
            Class<?> clazz = Class.forName("com.example.reflection.realize.TestReflect");
            // 调用TestReflect类中的reflect1方法
            Method method = clazz.getMethod("reflect1");
            method.invoke(clazz.newInstance());

            // Java 反射机制 - 调用某个类的方法1.
            // 调用TestReflect的reflect2方法
            method = clazz.getMethod("reflect2", int.class, String.class);
            method.invoke(clazz.newInstance(), 16, "Emily");
            // Java 反射机制 - 调用某个类的方法2.
            // age -> 16. name -> Emily
            System.out.println("----------------------------------------");
        } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
            e.printStackTrace();
        }

        // 8.通过反射机制操作某个类的属性
        setAssignAttribute();


        /**
         * 在java中有三种类类加载器。
         *
         * 1）Bootstrap ClassLoader 此加载器采用c++编写，一般开发中很少见。
         *
         * 2）Extension ClassLoader 用来进行扩展类的加载，一般对应的是jre lib ext目录中的类
         *
         * 3）AppClassLoader 加载classpath指定的类，是最常用的加载器。同时也是java中默认的加载器。
         *
         * 如果想要完成动态代理，首先需要定义一个InvocationHandler接口的子类，已完成代理的具体操作。
         *
         */
        System.out.println("9.反射机制的动态代理");
        MyInvocationHandler demo = new MyInvocationHandler();
        Subject sub = (Subject) demo.bind(new RealSubject());
        String info = sub.say("Emily", 16);
        String info1 = sub.str();
        System.out.println(info);
        System.out.println(info1);
        System.out.println("----------------------------------------");

        try {
            System.out.println("10.在泛型为Integer的ArrayList中存放一个String类型的对象");
            ArrayList<Integer> list = new ArrayList<>();
            Method method = list.getClass().getMethod("add", Object.class);
            System.out.println(method);
            method.invoke(list, "Java反射机制实例0");
            method.invoke(list, "Java反射机制实例1");

            System.out.println(list.get(1));
            // 结果 Java反射机制实例1
            System.out.println("----------------------------------------");
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }

        System.out.println("11.通过反射取得并修改数组的信息");
        int[] temp = {8, 2, 3, 4, 5};
        Class<?> demo1 = temp.getClass().getComponentType();
        System.out.println("数组类型： " + demo1.getName());
        System.out.println("数组长度  " + Array.getLength(temp));
        System.out.println("数组的第一个元素: " + Array.get(temp, 0));
        Array.set(temp, 0, 2);
        System.out.println("修改之后数组第一个元素为： " + Array.get(temp, 0));
        System.out.println("----------------------------------------");

        System.out.println("12.通过反射机制修改数组的大小");
        int[] temp1 = {11, 22, 3, 4, 5, 6, 7, 8, 9};
        int[] newTemp = (int[]) arrayInc(temp1, 15);
        print(newTemp);
        String[] atr = {"a", "b", "c"};
        String[] str1 = (String[]) arrayInc(atr, 8);
        print(str1);
        System.out.println("----------------------------------------");

        /**
         * 对于普通的工厂模式当我们在添加一个子类的时候，就需要对应的修改工厂类。 当我们添加很多的子类的时候，会很麻烦。
         * Java 工厂模式可以参考
         * http://baike.xsoftlab.net/view/java-factory-pattern
         *
         * 现在我们利用反射机制实现工厂模式，可以在不修改工厂类的情况下添加任意多个子类。
         *
         * 但是有一点仍然很麻烦，就是需要知道完整的包名和类名，这里可以使用properties配置文件来完成。
         *
         * java 读取 properties 配置文件 的方法可以参考
         * http://baike.xsoftlab.net/view/java-read-the-properties-configuration-file
         *
         */
        System.out.println("13.将反射机制应用于工厂模式");
        try {
            fruit f = Factory.getInstance("com.example.reflection.realize.Apple");
            if (f != null) {
                f.eat();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("------------------END-------------------");

    }

    /**
     * 修改数组大小
     */
    public static Object arrayInc(Object obj, int len) {
        Class<?> arr = obj.getClass().getComponentType();
        Object newArr = Array.newInstance(arr, len);
        int co = Array.getLength(obj);
        System.arraycopy(obj, 0, newArr, 0, co);
        return newArr;
    }

    /**
     * 打印
     */
    public static void print(Object obj) {
        Class<?> c = obj.getClass();
        if (!c.isArray()) {
            return;
        }
        System.out.println("数组长度为： " + Array.getLength(obj));
        for (int i = 0; i < Array.getLength(obj); i++) {
            System.out.print(Array.get(obj, i) + " ");
        }
        System.out.println();
    }

    /**
     * 通过一个对象获得完整的包名和类名 com.example.reflection.realize.TestReflect
     */
    public static void getPackageClassName() {
        TestReflect testReflect = new TestReflect();
        System.out.println("1.通过一个对象获得完整的包名和类名：\n" + testReflect.getClass().getName());
        System.out.println("----------------------------------------");
    }

    /**
     * 实例化Class类对象
     */
    public static void getInstantiationClassObject() {
        try {
            // 一般采用这种形式
            Class<?> class1 = Class.forName("com.example.reflection.realize.TestReflect");
            Class<?> class2 = new TestReflect().getClass();
            Class<?> class3 = TestReflect.class;

            System.out.println("2.实例化Class类对象：");
            System.out.println("类名称   " + class1.getName());
            System.out.println("类名称   " + class2.getName());
            System.out.println("类名称   " + class3.getName());
            System.out.println("----------------------------------------");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取一个对象的父类与实现的接口
     */
    public static void getInterfaceImplements() {
        try {
            Class<?> testReflect = Class.forName("com.example.reflection.realize.TestReflect");
            // 取得父类
            Class<?> parentClass = testReflect.getSuperclass();
            System.out.println("3.获取一个对象的父类与实现的接口:");
            System.out.println("TestReflect的父类为：" + parentClass.getName());
            // clazz的父类为： java.lang.Object
            // 获取所有的接口
            Class<?>[] interfaces = testReflect.getInterfaces();
            System.out.println("TestReflect实现的接口有：");
            for (int i = 0; i < interfaces.length; i++) {
                System.out.println((i + 1) + "." + interfaces[i].getName());
            }
            System.out.println("----------------------------------------");
            // TestReflect实现的接口有：
            // 1：java.io.Serializable
            // 2：com.example.reflection.service.IMath
            // 3：com.example.reflection.service.IMS
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过反射机制实例化一个类的对象
     */
    public static void getReflectionInstantiationClassObject() {
        try {
            Class<?> class1 = Class.forName("com.example.reflection.pojo.User");
            // 第一种方法，实例化默认构造方法，调用set赋值
            User user = (User) class1.newInstance();
            user.setAge(16);
            user.setName("Emily");
            System.out.println("4.通过反射机制实例化一个类的对象：");
            System.out.println(user);
            // 结果 User [age=16, name=Emily]
            // 第二种方法 取得全部的构造函数 使用构造函数赋值
            Constructor<?>[] constructors = class1.getConstructors();
            // 查看每个构造方法需要的参数
            for (int i = 0; i < constructors.length; i++) {
                Class<?>[] classes = constructors[i].getParameterTypes();
                System.out.print("constructors[" + i + "] (");
                for (int j = 0; j < classes.length; j++) {
                    if (j == classes.length - 1)
                        System.out.print(classes[j].getName());
                    else
                        System.out.print(classes[j].getName() + ",");
                }
                System.out.println(")");
            }
            // 结果
            // constructors[0] (int,java.lang.String)
            // constructors[1] (java.lang.String)
            // constructors[2] ()

            user = (User) constructors[1].newInstance("Emily");
            System.out.println(user);
            // 结果 User [age=0, name=Emily]
            user = (User) constructors[0].newInstance(16, "Emily");
            System.out.println(user);
            // 结果 User [age=16, name=Emily]
            System.out.println("----------------------------------------");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 获取某个类的全部属性
     */
    public static void getClassAllAttribute() {
        try {
            // getModifiers() 获取修饰符
            // getType().getName() 获取属性类型
            // getName() 获取成员属性名称
            // get(REFLECT) 获取属性值
            Class<?> testReflect = Class.forName("com.example.reflection.realize.TestReflect");
            // 取得本类的全部属性
            Field[] fields = testReflect.getDeclaredFields();
            System.out.println("5.获取某个类的全部属性:");
            System.out.println("本类所有成员属性：");
            fieldsResultSet(fields);

            System.out.println("获取某个类特定属性");
            Field field = testReflect.getDeclaredField("id");
            System.out.println(Modifier.toString(field.getModifiers()) + " " + field.getType().getName() + " " + field.getName() + " = " + field.get(REFLECT));

            System.out.println("本类或者父类的成员属性：");
            // 取得实现的接口或者父类的属性
            // fields数组中只包含了public修饰的成员属性，其中除了子类本身的public类型属性，也包括了父类的public类型属性。
            // fields数组中只包含了子类***的所有成员属性，不包含父类任何类型的成员属性
            Field[] fields1 = testReflect.getFields();
            fieldsResultSet(fields1);
            System.out.println("----------------------------------------");
        } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 遍历成员变量结果集
     *
     * @param fields
     */
    public static void fieldsResultSet(Field[] fields) {
        try {

            for (Field field : fields) {
                // 权限修饰符
                int mo = field.getModifiers();
                String modifier = Modifier.toString(mo);
                // 属性类型
                Class<?> type = field.getType();
                System.out.println(modifier + " " + type.getName() + " " + field.getName() + " = " + field.get(REFLECT) + ";");
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取某个类的全部方法
     */
    public static void getClassAllMethod() {
        try {
            System.out.println("6.获取某个类的全部方法");
            Class<?> clazz = Class.forName("com.example.reflection.realize.TestReflect");
            Method[] method = clazz.getMethods();
            for (Method value : method) {
                Class<?> returnType = value.getReturnType();
                Class<?>[] para = value.getParameterTypes();
                int temp = value.getModifiers();
                System.out.print(Modifier.toString(temp) + " ");
                System.out.print(returnType.getName() + "  ");
                System.out.print(value.getName() + " ");
                System.out.print("(");
                for (int j = 0; j < para.length; ++j) {
                    System.out.print(para[j].getName() + " " + "arg" + j);
                    if (j < para.length - 1) {
                        System.out.print(",");
                    }
                }
                Class<?>[] exce = value.getExceptionTypes();
                if (exce.length > 0) {
                    System.out.print(") throws ");
                    for (int k = 0; k < exce.length; ++k) {
                        System.out.print(exce[k].getName() + " ");
                        if (k < exce.length - 1) {
                            System.out.print(",");
                        }
                    }
                } else {
                    System.out.print(")");
                }
                System.out.println();
            }
            System.out.println("----------------------------------------");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public void reflect1() {
        System.out.println("Java 反射机制 - 调用某个类的方法1.");
    }

    public void reflect2(int age, String name) {
        System.out.println("Java 反射机制 - 调用某个类的方法2.");
        System.out.println("age -> " + age + ". name -> " + name);
    }

    /**
     * 通过反射机制操作某个类的属性
     */
    public static void setAssignAttribute() {
        try {
            System.out.println("8.通过反射机制操作某个类的属性");
            Class<?> testReflect = Class.forName("com.example.reflection.realize.TestReflect");
            Object obj = testReflect.newInstance();
            // 可以直接对 private 的属性赋值
            Field field = testReflect.getDeclaredField("str");
            field.setAccessible(true);
            System.out.print(Modifier.toString(field.getModifiers()) + " " + field.getType().getName().substring(10, 16) + " " + field.getName() + " = ");
            field.set(obj, "Java反射机制");
            System.out.println(field.get(obj));
            System.out.println("----------------------------------------");
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

}

// 反射机制的动态代理 START

/**
 * Subject接口
 */
interface Subject {
    String say(String name, int age);

    String str();
}

/**
 * 实现Subject接口
 */
class RealSubject implements Subject {
    public String say(String name, int age) {
        return name + "  " + age;
    }

    @Override
    public String str() {
        return "我是一个字符串";
    }
}

class MyInvocationHandler implements InvocationHandler {
    private Object obj = null;

    public Object bind(Object obj) {
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(this.obj, args);
    }
}
// 反射机制的动态代理 END

// 将反射机制应用于工厂模式 START
interface fruit {
    void eat();
}

class Apple implements fruit {
    public void eat() {
        System.out.println("Apple");
    }
}

class Orange implements fruit {
    public void eat() {
        System.out.println("Orange");
    }
}

class Factory {
    public static fruit getInstance(String ClassName) {
        fruit f = null;
        try {
            f = (fruit) Class.forName(ClassName).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return f;
    }
}
// 将反射机制应用于工厂模式 END